/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <stm32_psram.h>
#include <aps512xx.h>
#include <stm32_common.h>
#include <stm32_errno.h>
/* includes (standard library, system) ---------------------------------------*/
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <hal_mtd.h>
#include <hal_it.h>

/* defines -------------------------------------------------------------------*/

#define BSP_HSPI_RAM_BurstLength_t      APS512XX_BurstLength_t
#define BSP_HSPI_RAM_ReadLatencyCode_t  APS512XX_ReadLatencyCode_t
#define BSP_HSPI_RAM_WriteLatencyCode_t APS512XX_WriteLatencyCode_t

/* HSPI RAM interrupt priority */
#define BSP_HSPI_RAM_IT_PRIORITY     0x0FUL /* Default is lowest priority level */
#define BSP_HSPI_RAM_DMA_IT_PRIORITY 0x0FUL /* Default is lowest priority level */

/* Definition for HSPI clock resources */
#define HSPI_CLK_ENABLE()  __HAL_RCC_HSPI1_CLK_ENABLE()
#define HSPI_CLK_DISABLE() __HAL_RCC_HSPI1_CLK_DISABLE()

#define HSPI_CLK_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_NCLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_DQS0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_DQS1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_CS_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D4_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D5_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOH_CLK_ENABLE()
#define HSPI_D6_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D7_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D8_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D9_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D10_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D11_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D12_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D13_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D14_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOI_CLK_ENABLE()
#define HSPI_D15_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOJ_CLK_ENABLE()

#define HSPI_FORCE_RESET()   __HAL_RCC_HSPI1_FORCE_RESET()
#define HSPI_RELEASE_RESET() __HAL_RCC_HSPI1_RELEASE_RESET()

/* Definition for HSPI Pins */
/* HSPI_CLK */
#define HSPI_CLK_PIN        GPIO_PIN_3
#define HSPI_CLK_GPIO_PORT  GPIOI
#define HSPI_CLK_PIN_AF     GPIO_AF8_HSPI1
/* HSPI_NCLK */
#define HSPI_NCLK_PIN       GPIO_PIN_4
#define HSPI_NCLK_GPIO_PORT GPIOI
#define HSPI_NCLK_PIN_AF    GPIO_AF8_HSPI1
/* HSPI_DQS0 */
#define HSPI_DQS0_PIN       GPIO_PIN_2
#define HSPI_DQS0_GPIO_PORT GPIOI
#define HSPI_DQS0_PIN_AF    GPIO_AF8_HSPI1
/* HSPI_DQS1 */
#define HSPI_DQS1_PIN       GPIO_PIN_8
#define HSPI_DQS1_GPIO_PORT GPIOI
#define HSPI_DQS1_PIN_AF    GPIO_AF8_HSPI1
/* HSPI_CS */
#define HSPI_CS_PIN         GPIO_PIN_9
#define HSPI_CS_GPIO_PORT   GPIOH
#define HSPI_CS_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D0 */
#define HSPI_D0_PIN         GPIO_PIN_10
#define HSPI_D0_GPIO_PORT   GPIOH
#define HSPI_D0_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D1 */
#define HSPI_D1_PIN         GPIO_PIN_11
#define HSPI_D1_GPIO_PORT   GPIOH
#define HSPI_D1_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D2 */
#define HSPI_D2_PIN         GPIO_PIN_12
#define HSPI_D2_GPIO_PORT   GPIOH
#define HSPI_D2_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D3 */
#define HSPI_D3_PIN         GPIO_PIN_13
#define HSPI_D3_GPIO_PORT   GPIOH
#define HSPI_D3_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D4 */
#define HSPI_D4_PIN         GPIO_PIN_14
#define HSPI_D4_GPIO_PORT   GPIOH
#define HSPI_D4_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D5 */
#define HSPI_D5_PIN         GPIO_PIN_15
#define HSPI_D5_GPIO_PORT   GPIOH
#define HSPI_D5_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D6 */
#define HSPI_D6_PIN         GPIO_PIN_0
#define HSPI_D6_GPIO_PORT   GPIOI
#define HSPI_D6_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D7 */
#define HSPI_D7_PIN         GPIO_PIN_1
#define HSPI_D7_GPIO_PORT   GPIOI
#define HSPI_D7_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D8 */
#define HSPI_D8_PIN         GPIO_PIN_9
#define HSPI_D8_GPIO_PORT   GPIOI
#define HSPI_D8_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D9 */
#define HSPI_D9_PIN         GPIO_PIN_10
#define HSPI_D9_GPIO_PORT   GPIOI
#define HSPI_D9_PIN_AF      GPIO_AF8_HSPI1
/* HSPI_D10 */
#define HSPI_D10_PIN        GPIO_PIN_11
#define HSPI_D10_GPIO_PORT  GPIOI
#define HSPI_D10_PIN_AF     GPIO_AF8_HSPI1
/* HSPI_D11 */
#define HSPI_D11_PIN        GPIO_PIN_12
#define HSPI_D11_GPIO_PORT  GPIOI
#define HSPI_D11_PIN_AF     GPIO_AF8_HSPI1
/* HSPI_D12 */
#define HSPI_D12_PIN        GPIO_PIN_13
#define HSPI_D12_GPIO_PORT  GPIOI
#define HSPI_D12_PIN_AF     GPIO_AF8_HSPI1
/* HSPI_D13 */
#define HSPI_D13_PIN        GPIO_PIN_14
#define HSPI_D13_GPIO_PORT  GPIOI
#define HSPI_D13_PIN_AF     GPIO_AF8_HSPI1
/* HSPI_D14 */
#define HSPI_D14_PIN        GPIO_PIN_15
#define HSPI_D14_GPIO_PORT  GPIOI
#define HSPI_D14_PIN_AF     GPIO_AF8_HSPI1
/* HSPI_D15 */
#define HSPI_D15_PIN        GPIO_PIN_0
#define HSPI_D15_GPIO_PORT  GPIOJ
#define HSPI_D15_PIN_AF     GPIO_AF8_HSPI1

/* HSPI Burst length */
#define BSP_HSPI_RAM_BURST_16_BYTES APS512XX_BURST_16_BYTES
#define BSP_HSPI_RAM_BURST_32_BYTES APS512XX_BURST_32_BYTES
#define BSP_HSPI_RAM_BURST_64_BYTES APS512XX_BURST_64_BYTES
#define BSP_HSPI_RAM_BURST_2K_BYTES APS512XX_BURST_2K_BYTES

/* HSPI Read Latency Code */
#define BSP_HSPI_RAM_READ_LATENCY_3 APS512XX_READ_LATENCY_3
#define BSP_HSPI_RAM_READ_LATENCY_4 APS512XX_READ_LATENCY_4
#define BSP_HSPI_RAM_READ_LATENCY_5 APS512XX_READ_LATENCY_5
#define BSP_HSPI_RAM_READ_LATENCY_6 APS512XX_READ_LATENCY_6
#define BSP_HSPI_RAM_READ_LATENCY_7 APS512XX_READ_LATENCY_7

/* HSPI Write Latency Code */
#define BSP_HSPI_RAM_WRITE_LATENCY_3 APS512XX_WRITE_LATENCY_3
#define BSP_HSPI_RAM_WRITE_LATENCY_4 APS512XX_WRITE_LATENCY_4
#define BSP_HSPI_RAM_WRITE_LATENCY_5 APS512XX_WRITE_LATENCY_5
#define BSP_HSPI_RAM_WRITE_LATENCY_6 APS512XX_WRITE_LATENCY_6
#define BSP_HSPI_RAM_WRITE_LATENCY_7 APS512XX_WRITE_LATENCY_7

/* DMA definitions for HSPI RAM DMA transfer */
#define HSPI_RAM_DMAx_CLK_ENABLE  __HAL_RCC_GPDMA1_CLK_ENABLE
#define HSPI_RAM_DMAx_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE
#define HSPI_RAM_DMAx_CHANNEL     GPDMA1_Channel2
#define HSPI_RAM_DMAx_IRQn        GPDMA1_Channel2_IRQn
#define HPSI_RAM_DMAx_IRQHandler  GPDMA1_Channel2_IRQHandler

/* typedefs ------------------------------------------------------------------*/

typedef enum {
    HSPI_ACCESS_NONE = 0, /*!<  Instance not initialized,              */
    HSPI_ACCESS_INDIRECT, /*!<  Instance use indirect mode access      */
    HSPI_ACCESS_MMP       /*!<  Instance use Memory Mapped Mode        */
} HSPI_Access_t;

typedef struct {
    uint32_t MemorySize;
    uint32_t ClockPrescaler;
    uint32_t SampleShifting;
    uint32_t TransferRate;
} MX_HSPI_InitTypeDef;

typedef enum {
    BSP_HSPI_RAM_VARIABLE_LATENCY = 0,
    BSP_HSPI_RAM_FIXED_LATENCY = APS512XX_MR0_LATENCY_TYPE
} BSP_HSPI_RAM_LatencyType_t;

typedef enum {
    BSP_HSPI_RAM_LINEAR_BURST = 0,
    BSP_HSPI_RAM_HYBRID_BURST = APS512XX_MR8_BT
} BSP_HSPI_RAM_BurstType_t;

typedef enum {
    BSP_HSPI_RAM_IO_X8_MODE = 0,
    BSP_HSPI_RAM_IO_X16_MODE = APS512XX_MR8_X8_X16,
} BSP_HSPI_RAM_IOMode_t;

typedef struct {
    HSPI_Access_t IsInitialized;                      /*!< Instance access Flash method   */
    BSP_HSPI_RAM_LatencyType_t LatencyType;           /*!< Latency Type of Instance       */
    BSP_HSPI_RAM_ReadLatencyCode_t ReadLatencyCode;   /*!< Read Latency Code of Instance  */
    BSP_HSPI_RAM_WriteLatencyCode_t WriteLatencyCode; /*!< Write Latency Code of Instance */
    BSP_HSPI_RAM_BurstType_t BurstType;               /*!< Burst Type of Instance         */
    BSP_HSPI_RAM_BurstLength_t BurstLength;           /*!< Burst Length of Instance       */
    BSP_HSPI_RAM_IOMode_t IOMode;                     /*!< IO Mode of Instance            */
} HSPI_RAM_Ctx_t;

typedef struct psram_dev {
    hal_mtd_t mtd;
    XSPI_HandleTypeDef handle;
    HSPI_RAM_Ctx_t ctx;
} psram_dev_t;

/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/

static psram_dev_t psram_dev;

/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

#if defined(CONFIG_PSRAM_USE_DMA)

void __psram_dmarx_it_handler(uintptr_t arg)
{
    psram_dev_t *dev = (psram_dev_t *)arg;
    HAL_DMA_IRQHandler(dev->handle.hdmarx);
}

void __psram_dmatx_it_handler(uintptr_t arg)
{
    psram_dev_t *dev = (psram_dev_t *)arg;
    HAL_DMA_IRQHandler(dev->handle.hdmatx);
}

void __psram_dma_it_handler(uintptr_t arg)
{
    psram_dev_t *dev = (psram_dev_t *)arg;
    HAL_XSPI_IRQHandler(&dev->handle);
}

void __psram_hspi_it_handler(uintptr_t arg)
{
    psram_dev_t *dev = (psram_dev_t *)arg;
    HAL_XSPI_IRQHandler(&dev->handle);
}

#endif

static void HSPI_RAM_MspInit(psram_dev_t *dev)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    __HAL_RCC_PWR_CLK_ENABLE();
    SET_BIT(PWR->SVMCR, PWR_SVMCR_IO2SV);

    /* Enable the HSPI memory interface, DMA and GPIO clocks */
    HSPI_CLK_ENABLE();
#if defined(CONFIG_PSRAM_USE_DMA)
    HSPI_RAM_DMAx_CLK_ENABLE();
#endif
    HSPI_CLK_GPIO_CLK_ENABLE();
    HSPI_DQS0_GPIO_CLK_ENABLE();
    HSPI_DQS1_GPIO_CLK_ENABLE();
    HSPI_CS_GPIO_CLK_ENABLE();
    HSPI_D0_GPIO_CLK_ENABLE();
    HSPI_D1_GPIO_CLK_ENABLE();
    HSPI_D2_GPIO_CLK_ENABLE();
    HSPI_D3_GPIO_CLK_ENABLE();
    HSPI_D4_GPIO_CLK_ENABLE();
    HSPI_D5_GPIO_CLK_ENABLE();
    HSPI_D6_GPIO_CLK_ENABLE();
    HSPI_D7_GPIO_CLK_ENABLE();
    HSPI_D8_GPIO_CLK_ENABLE();
    HSPI_D9_GPIO_CLK_ENABLE();
    HSPI_D10_GPIO_CLK_ENABLE();
    HSPI_D11_GPIO_CLK_ENABLE();
    HSPI_D12_GPIO_CLK_ENABLE();
    HSPI_D13_GPIO_CLK_ENABLE();
    HSPI_D14_GPIO_CLK_ENABLE();
    HSPI_D15_GPIO_CLK_ENABLE();

    /*  Activate HSLV  */
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_CS_GPIO_PORT, HSPI_CS_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_DQS0_GPIO_PORT, HSPI_DQS0_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_DQS1_GPIO_PORT, HSPI_DQS1_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_CLK_GPIO_PORT, HSPI_CLK_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_NCLK_GPIO_PORT, HSPI_NCLK_PIN);

    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D0_GPIO_PORT, HSPI_D0_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D1_GPIO_PORT, HSPI_D1_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D2_GPIO_PORT, HSPI_D2_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D3_GPIO_PORT, HSPI_D3_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D4_GPIO_PORT, HSPI_D4_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D5_GPIO_PORT, HSPI_D5_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D6_GPIO_PORT, HSPI_D6_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D7_GPIO_PORT, HSPI_D7_PIN);

    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D8_GPIO_PORT, HSPI_D8_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D9_GPIO_PORT, HSPI_D9_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D10_GPIO_PORT, HSPI_D10_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D11_GPIO_PORT, HSPI_D11_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D12_GPIO_PORT, HSPI_D12_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D13_GPIO_PORT, HSPI_D13_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D14_GPIO_PORT, HSPI_D14_PIN);
    HAL_GPIO_EnableHighSPeedLowVoltage(HSPI_D15_GPIO_PORT, HSPI_D15_PIN);

    /* Reset the HSPI memory interface */
    HSPI_FORCE_RESET();
    HSPI_RELEASE_RESET();

    /* HSPI CS GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_CS_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = HSPI_CS_PIN_AF;
    HAL_GPIO_Init(HSPI_CS_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI DQS0 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_DQS0_PIN;
    GPIO_InitStruct.Alternate = HSPI_DQS0_PIN_AF;
    HAL_GPIO_Init(HSPI_DQS0_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI DQS1 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_DQS1_PIN;
    GPIO_InitStruct.Alternate = HSPI_DQS1_PIN_AF;
    HAL_GPIO_Init(HSPI_DQS1_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI CLK GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_CLK_PIN;
    GPIO_InitStruct.Alternate = HSPI_CLK_PIN_AF;
    HAL_GPIO_Init(HSPI_CLK_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D0 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D0_PIN;
    GPIO_InitStruct.Alternate = HSPI_D0_PIN_AF;
    HAL_GPIO_Init(HSPI_D0_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D1 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D1_PIN;
    GPIO_InitStruct.Alternate = HSPI_D1_PIN_AF;
    HAL_GPIO_Init(HSPI_D1_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D2 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D2_PIN;
    GPIO_InitStruct.Alternate = HSPI_D2_PIN_AF;
    HAL_GPIO_Init(HSPI_D2_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D3 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D3_PIN;
    GPIO_InitStruct.Alternate = HSPI_D3_PIN_AF;
    HAL_GPIO_Init(HSPI_D3_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D4 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D4_PIN;
    GPIO_InitStruct.Alternate = HSPI_D4_PIN_AF;
    HAL_GPIO_Init(HSPI_D4_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D5 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D5_PIN;
    GPIO_InitStruct.Alternate = HSPI_D5_PIN_AF;
    HAL_GPIO_Init(HSPI_D5_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D6 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D6_PIN;
    GPIO_InitStruct.Alternate = HSPI_D6_PIN_AF;
    HAL_GPIO_Init(HSPI_D6_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D7 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D7_PIN;
    GPIO_InitStruct.Alternate = HSPI_D7_PIN_AF;
    HAL_GPIO_Init(HSPI_D7_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D8 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D8_PIN;
    GPIO_InitStruct.Alternate = HSPI_D8_PIN_AF;
    HAL_GPIO_Init(HSPI_D8_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D9 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D9_PIN;
    GPIO_InitStruct.Alternate = HSPI_D9_PIN_AF;
    HAL_GPIO_Init(HSPI_D9_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D10 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D10_PIN;
    GPIO_InitStruct.Alternate = HSPI_D10_PIN_AF;
    HAL_GPIO_Init(HSPI_D10_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D11 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D11_PIN;
    GPIO_InitStruct.Alternate = HSPI_D11_PIN_AF;
    HAL_GPIO_Init(HSPI_D11_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D12 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D12_PIN;
    GPIO_InitStruct.Alternate = HSPI_D12_PIN_AF;
    HAL_GPIO_Init(HSPI_D12_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D13 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D13_PIN;
    GPIO_InitStruct.Alternate = HSPI_D13_PIN_AF;
    HAL_GPIO_Init(HSPI_D13_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D14 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D14_PIN;
    GPIO_InitStruct.Alternate = HSPI_D14_PIN_AF;
    HAL_GPIO_Init(HSPI_D14_GPIO_PORT, &GPIO_InitStruct);

    /* HSPI D15 GPIO pin configuration  */
    GPIO_InitStruct.Pin = HSPI_D15_PIN;
    GPIO_InitStruct.Alternate = HSPI_D15_PIN_AF;
    HAL_GPIO_Init(HSPI_D15_GPIO_PORT, &GPIO_InitStruct);

#if defined(CONFIG_PSRAM_USE_DMA)
    static DMA_HandleTypeDef hdmatx, hdmarx;
    XSPI_HandleTypeDef *hhspi = &dev->handle;

    hdmatx.Init.Request = GPDMA1_REQUEST_HSPI1;
    hdmatx.Instance = GPDMA1_Channel12;
    hal_it_connect(GPDMA1_Channel12_IRQn, __psram_dmatx_it_handler, (uintptr_t)dev);
    hal_it_setprio(GPDMA1_Channel12_IRQn, 0x00);
    hal_it_enable(GPDMA1_Channel12_IRQn);

    /* Configure the HSPI DMA transmit */
    hdmatx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdmatx.Init.SrcInc = DMA_SINC_INCREMENTED;
    hdmatx.Init.DestInc = DMA_DINC_FIXED;
    hdmatx.Init.Priority = DMA_HIGH_PRIORITY;
    hdmatx.Init.SrcBurstLength = 1;
    hdmatx.Init.DestBurstLength = 1;
    hdmatx.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    hdmatx.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
    hdmatx.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;

    /* Enable the GPDMA clock */
    __HAL_RCC_GPDMA1_CLK_ENABLE();

    __HAL_LINKDMA(hhspi, hdmatx, hdmatx);

    /* Initialize the DMA channel */
    (void)HAL_DMA_Init(&hdmatx);

    hdmarx.Init.Request = GPDMA1_REQUEST_HSPI1;
    hdmarx.Instance = GPDMA1_Channel7;
    hal_it_connect(GPDMA1_Channel7_IRQn, __psram_dmarx_it_handler, (uintptr_t)dev);
    hal_it_setprio(GPDMA1_Channel7_IRQn, 0x00);
    hal_it_enable(GPDMA1_Channel7_IRQn);

    /* Configure the HSPI DMA receive */
    hdmarx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdmarx.Init.SrcInc = DMA_SINC_FIXED;
    hdmarx.Init.DestInc = DMA_DINC_INCREMENTED;
    hdmarx.Init.Priority = DMA_HIGH_PRIORITY;
    hdmarx.Init.SrcBurstLength = 1;
    hdmarx.Init.DestBurstLength = 1;
    hdmarx.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    hdmarx.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
    hdmarx.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;

    /* Enable the GPDMA clock */
    __HAL_RCC_GPDMA1_CLK_ENABLE();

    __HAL_LINKDMA(hhspi, hdmarx, hdmarx);

    /* Initialize the DMA channel */
    (void)HAL_DMA_Init(&hdmarx);

    /* Enable and set priority of the HSPI and DMA interrupts */

    hal_it_connect(HSPI1_IRQn, __psram_hspi_it_handler, (uintptr_t)dev);
    hal_it_setprio(HSPI1_IRQn, BSP_HSPI_RAM_IT_PRIORITY);
    hal_it_enable(HSPI1_IRQn);

    hal_it_connect(HSPI_RAM_DMAx_IRQn, __psram_dma_it_handler, (uintptr_t)dev);
    hal_it_setprio(HSPI_RAM_DMAx_IRQn, BSP_HSPI_RAM_DMA_IT_PRIORITY);
    hal_it_enable(HSPI_RAM_DMAx_IRQn);
#endif
}

__weak HAL_StatusTypeDef MX_HSPI_RAM_Init(XSPI_HandleTypeDef *hhspi, MX_HSPI_InitTypeDef *Init)
{
    HAL_StatusTypeDef status;
    uint32_t hspi_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_HSPI);

    /* HSPI initialization */
    hhspi->Instance = HSPI1;

#if defined(CONFIG_PSRAM_USE_DMA)
    hhspi->Init.FifoThresholdByte = 4U;
#else
    hhspi->Init.FifoThresholdByte = 2U;
#endif
    hhspi->Init.MemoryMode = HAL_XSPI_SINGLE_MEM;
    hhspi->Init.MemoryType = HAL_XSPI_MEMTYPE_APMEM_16BITS;
    hhspi->Init.MemorySize = Init->MemorySize;
    hhspi->Init.ChipSelectHighTimeCycle = 1U;
    hhspi->Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
    hhspi->Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
    hhspi->Init.WrapSize = HAL_XSPI_WRAP_32_BYTES;
    hhspi->Init.ClockPrescaler = Init->ClockPrescaler;
    hhspi->Init.SampleShifting = Init->SampleShifting;
    hhspi->Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_DISABLE;
    hhspi->Init.ChipSelectBoundary = 11U;
    hhspi->Init.DelayBlockBypass = HAL_XSPI_DELAY_BLOCK_ON;
    /* tCEM = 2 us max => REFRESH+4 clock cycles for read */
    hhspi->Init.Refresh = ((2U * (hspi_clk / (Init->ClockPrescaler + 1U))) / 1000000U) - 4U;
    hhspi->Init.MaxTran = 0U;

    status = HAL_XSPI_Init(hhspi);

    return status;
}

static int32_t BSP_HSPI_RAM_EnableMemoryMappedMode(psram_dev_t *dev)
{
    XSPI_HandleTypeDef *hhspi = &dev->handle;
    HSPI_RAM_Ctx_t *ctx = &dev->ctx;
    int32_t ret = BSP_ERROR_NONE;

    if (APS512XX_EnableMemoryMappedMode(
            hhspi,
            (uint32_t)APS512XX_READ_LATENCY((uint32_t)((uint32_t)(ctx->ReadLatencyCode)),
                                            (uint32_t)(ctx->LatencyType)),
            APS512XX_WRITE_LATENCY((uint32_t)(ctx->WriteLatencyCode)), (uint32_t)(ctx->IOMode),
            (uint32_t)(ctx->BurstType)) != (int32_t)APS512XX_OK) {
        ret = BSP_ERROR_COMPONENT_FAILURE;
    } else {
        /* Update HSPI context if all operations are well done */
        ctx->IsInitialized = HSPI_ACCESS_MMP;
    }

    /* Return BSP status */
    return ret;
}

static int32_t BSP_HSPI_RAM_DisableMemoryMappedMode(psram_dev_t *dev)
{
    XSPI_HandleTypeDef *hhspi = &dev->handle;
    HSPI_RAM_Ctx_t *ctx = &dev->ctx;
    int32_t ret = BSP_ERROR_NONE;
    uint32_t tickstart = HAL_GetTick();

    if (ctx->IsInitialized != HSPI_ACCESS_MMP) {
        ret = BSP_ERROR_HSPI_MMP_UNLOCK_FAILURE;
    } else if (HAL_XSPI_Abort(hhspi) != HAL_OK) {
        /* Abort MMP back to indirect mode */
        ret = BSP_ERROR_PERIPH_FAILURE;
    } else {
        /* Update HSPI 16-bits Octal RAM context if all operations are well done */
        /* Wait until flag is in expected state */
        while ((HAL_XSPI_GET_FLAG(hhspi, HAL_XSPI_FLAG_BUSY)) != RESET) {
            /* Check for the Timeout */
            if (((HAL_GetTick() - tickstart) > hhspi->Timeout) || (hhspi->Timeout == 0U)) {
                ret = BSP_ERROR_PERIPH_FAILURE;
                break;
            }
        }

        if (ret == BSP_ERROR_NONE) {
            /* Configure CR register with functional mode as indirect mode*/
            MODIFY_REG(hhspi->Instance->CR, (HSPI_CR_FMODE), 0U);
            ctx->IsInitialized = HSPI_ACCESS_INDIRECT;
        }
    }

    /* Return BSP status */
    return ret;
}

/**
 * @brief  Set 16-bits Octal RAM to desired configuration. And this instance becomes current
 * instance. If current instance running at MMP mode then this function doesn't work. Indirect ->
 * Indirect
 */
static int BSP_HSPI_RAM_Config16BitsOctalRAM(psram_dev_t *dev, HSPI_RAM_Ctx_t *cfg)
{
    HSPI_RAM_Ctx_t *ctx = &dev->ctx;
    XSPI_HandleTypeDef *hhspi = &dev->handle;
    uint8_t reg[2];

    if (cfg->BurstLength == APS512XX_BURST_2_KBYTES) {
        return -1;
    }
    /* Check if MMP mode locked ***********************************************/
    if (ctx->IsInitialized == HSPI_ACCESS_MMP) {
        return -2;
    }
    if ((ctx->IsInitialized == HSPI_ACCESS_NONE) || (ctx->LatencyType != cfg->LatencyType) ||
        (ctx->ReadLatencyCode != cfg->ReadLatencyCode)) {
        /* Reading the configuration of Mode Register 0 ***********************/
        if (APS512XX_ReadReg(hhspi, APS512XX_MR0_ADDRESS, reg,
                             (uint32_t)APS512XX_READ_REG_LATENCY(
                                 (uint32_t)(ctx->ReadLatencyCode))) != (int32_t)APS512XX_OK) {
            return -3;
        } else {
            /* Configure the 16-bits Octal RAM memory ***************************/
            MODIFY_REG(reg[0],
                       ((uint8_t)APS512XX_MR0_LATENCY_TYPE |
                        (uint8_t)APS512XX_MR0_READ_LATENCY_CODE |
                        (uint8_t)APS512XX_MR0_DRIVE_STRENGTH),
                       ((uint8_t)(cfg->LatencyType) | (uint8_t)(cfg->ReadLatencyCode) |
                        (uint8_t)CONF_HSPI_DS));

            if (APS512XX_WriteReg(hhspi, APS512XX_MR0_ADDRESS, reg[0]) != APS512XX_OK) {
                return -4;
            }
            /* Configure the peripheral *****************************************/
        }
    }

    if ((ctx->IsInitialized == HSPI_ACCESS_NONE) ||
        (ctx->WriteLatencyCode != cfg->WriteLatencyCode)) {
        /* Reading the configuration of Mode Register 4 ***********************/
        if (APS512XX_ReadReg(hhspi, APS512XX_MR4_ADDRESS, reg,
                             (uint32_t)APS512XX_READ_REG_LATENCY(
                                 (uint32_t)(ctx->ReadLatencyCode))) != (int32_t)APS512XX_OK) {
            return -5;
        } else {
            /* Configure the 16-bits Octal RAM memory ***************************/
            MODIFY_REG(reg[0],
                       (uint8_t)(APS512XX_MR4_WRITE_LATENCY_CODE | APS512XX_MR4_RF_RATE |
                                 APS512XX_MR4_PASR),
                       ((uint8_t)cfg->WriteLatencyCode | CONF_HSPI_RF | CONF_HSPI_PASR));

            if (APS512XX_WriteReg(hhspi, APS512XX_MR4_ADDRESS, reg[0]) != APS512XX_OK) {
                return -6;
            }
        }
    }

    if ((ctx->IsInitialized == HSPI_ACCESS_NONE) || (ctx->BurstType != cfg->BurstType) ||
        (ctx->BurstLength != cfg->BurstLength) || (ctx->IOMode != cfg->IOMode)) {
        /* Reading the configuration of Mode Register 8 ***********************/
        if (APS512XX_ReadReg(hhspi, APS512XX_MR8_ADDRESS, reg,
                             (uint32_t)APS512XX_READ_REG_LATENCY(
                                 (uint32_t)(ctx->ReadLatencyCode))) != (int32_t)APS512XX_OK) {
            return -7;
        } else {
            /* Configure the 16-bits Octal RAM memory ***************************/
            MODIFY_REG(reg[0], ((uint8_t)APS512XX_MR8_X8_X16 | (uint8_t)APS512XX_MR8_BL),
                       ((uint8_t)(cfg->IOMode) | (uint8_t)(cfg->BurstLength)));

            if (APS512XX_WriteReg(hhspi, APS512XX_MR8_ADDRESS, reg[0]) != APS512XX_OK) {
                return -8;
            } else {
                /* Configure the peripheral *****************************************/
                if (cfg->BurstType == BSP_HSPI_RAM_HYBRID_BURST) {
                    switch (cfg->BurstLength) {
                    case APS512XX_MR8_BL_16_BYTES:
                        hhspi->Init.WrapSize = HAL_XSPI_WRAP_16_BYTES;
                        break;
                    case APS512XX_MR8_BL_32_BYTES:
                        hhspi->Init.WrapSize = HAL_XSPI_WRAP_32_BYTES;
                        break;
                    case APS512XX_MR8_BL_64_BYTES:
                        hhspi->Init.WrapSize = HAL_XSPI_WRAP_64_BYTES;
                        break;
                    default:
                        break;
                    }
                } else {
                    hhspi->Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
                }

                if (cfg->IOMode == BSP_HSPI_RAM_IO_X16_MODE) {
                    hhspi->Init.MemoryType = HAL_XSPI_MEMTYPE_APMEM_16BITS;
                } else {
                    hhspi->Init.MemoryType = HAL_XSPI_MEMTYPE_APMEM;
                }

                if (HAL_XSPI_DeInit(hhspi) != HAL_OK) {
                    return -9;
                }

                if (HAL_XSPI_Init(hhspi) != HAL_OK) {
                    return -10;
                }
            }
        }
    }

    /* Update HSPI context if all operations are well done */
    /* Update current status parameter *****************************************/
    ctx->IsInitialized = HSPI_ACCESS_INDIRECT;
    ctx->LatencyType = cfg->LatencyType;
    ctx->ReadLatencyCode = cfg->ReadLatencyCode;
    ctx->WriteLatencyCode = cfg->WriteLatencyCode;
    ctx->BurstType = cfg->BurstType;
    ctx->BurstLength = cfg->BurstLength;
    ctx->IOMode = cfg->IOMode;

    /* Return BSP status */
    return 0;
}

static int __psram_config(psram_dev_t *dev)
{
    HSPI_RAM_Ctx_t *ctx = &dev->ctx;

    ctx->IsInitialized = HSPI_ACCESS_NONE;
    ctx->LatencyType = BSP_HSPI_RAM_VARIABLE_LATENCY;
    ctx->ReadLatencyCode = DEFAULT_READ_LATENCY_CODE;
    ctx->WriteLatencyCode = DEFAULT_WRITE_LATENCY_CODE;
    ctx->BurstType = BSP_HSPI_RAM_HYBRID_BURST;
    ctx->BurstLength = BSP_HSPI_RAM_BURST_32_BYTES;
    ctx->IOMode = BSP_HSPI_RAM_IO_X8_MODE;

    HSPI_RAM_Ctx_t cfg;
    // Fixed Latency 2*5, Hybrid 32-bytes burst, x16 IO
    cfg.LatencyType = BSP_HSPI_RAM_FIXED_LATENCY;
    cfg.BurstType = BSP_HSPI_RAM_LINEAR_BURST;
    cfg.BurstLength = BSP_HSPI_RAM_BURST_32_BYTES;
    cfg.ReadLatencyCode = BSP_HSPI_RAM_READ_LATENCY_5;
    cfg.WriteLatencyCode = BSP_HSPI_RAM_WRITE_LATENCY_5;
    cfg.IOMode = BSP_HSPI_RAM_IO_X16_MODE;

    return BSP_HSPI_RAM_Config16BitsOctalRAM(dev, &cfg);
}

static int __stm32_psram_init(psram_dev_t *dev)
{
    XSPI_HandleTypeDef *hhspi = &dev->handle;
    MX_HSPI_InitTypeDef hspi_init;

    HSPI_RAM_MspInit(dev);

    /* Fill config structure */
    hspi_init.ClockPrescaler = 0; /* HSPI clock = 160MHz / ClockPrescaler = 160MHz */
    hspi_init.MemorySize = (uint32_t)POSITION_VAL(APS512XX_RAM_SIZE);
    hspi_init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE;

    /* STM32 HSPI interface initialization */
    if (MX_HSPI_RAM_Init(hhspi, &hspi_init) != HAL_OK) {
        return -1;
    }
    /* Reset the memory */
    if (APS512XX_Reset(hhspi) != 0) {
        return -2;
    }
    if (__psram_config(dev) != 0) {
        return -3;
    }

    return 0;
}

static int __psram_read(hal_mtd_t *mtd, size_t pos, size_t nbytes, uint8_t *buf)
{
    psram_dev_t *dev = container_of(mtd, psram_dev_t, mtd);
    XSPI_HandleTypeDef *hhspi = &dev->handle;
    HSPI_RAM_Ctx_t *ctx = &dev->ctx;

#if defined(CONFIG_PSRAM_USE_DMA)
    if (APS512XX_Read_DMA(hhspi, buf, pos, nbytes,
                          (uint32_t)APS512XX_READ_LATENCY((uint32_t)(ctx->ReadLatencyCode),
                                                          (uint32_t)(ctx->LatencyType)),
                          (uint32_t)(ctx->IOMode),
                          (uint32_t)(ctx->BurstType)) != (int32_t)APS512XX_OK) {
        return -1;
    }
#else
    if (APS512XX_Read(hhspi, buf, pos, nbytes,
                      (uint32_t)APS512XX_READ_LATENCY((uint32_t)(ctx->ReadLatencyCode),
                                                      (uint32_t)(ctx->LatencyType)),
                      (uint32_t)(ctx->IOMode),
                      (uint32_t)(ctx->BurstType)) != (int32_t)APS512XX_OK) {
        return -1;
    }
#endif
    return 0;
}

static int __psram_write(hal_mtd_t *mtd, size_t pos, size_t nbytes, const uint8_t *buf)
{
    psram_dev_t *dev = container_of(mtd, psram_dev_t, mtd);
    XSPI_HandleTypeDef *hhspi = &dev->handle;
    HSPI_RAM_Ctx_t *ctx = &dev->ctx;

#if defined(CONFIG_PSRAM_USE_DMA)
    if (APS512XX_Write_DMA(hhspi, (uint8_t *)buf, pos, nbytes,
                           (uint32_t)APS512XX_WRITE_LATENCY((uint32_t)(ctx->WriteLatencyCode)),
                           (uint32_t)(ctx->IOMode),
                           (uint32_t)(ctx->BurstType)) != (int32_t)APS512XX_OK) {
        return -1;
    }
#else
    if (APS512XX_Write(hhspi, (uint8_t *)buf, pos, nbytes,
                       (uint32_t)APS512XX_WRITE_LATENCY((uint32_t)(ctx->WriteLatencyCode)),
                       (uint32_t)(ctx->IOMode),
                       (uint32_t)(ctx->BurstType)) != (int32_t)APS512XX_OK) {
        return -1;
    }
#endif
    return 0;
}

static int __psram_ioctl(hal_mtd_t *mtd, int cmd, va_list valist)
{
    psram_dev_t *dev = container_of(mtd, psram_dev_t, mtd);
    int ret = -ENOSYS;

    switch (cmd) {
    case PSRAM_CMD_MEMORY_MAPPED: {
        size_t flag = va_arg(valist, size_t);
        if (flag == 0) {
            return BSP_HSPI_RAM_DisableMemoryMappedMode(dev);
        } else {
            return BSP_HSPI_RAM_EnableMemoryMappedMode(dev);
        }
    }
    default:
        break;
    }

    return ret;
}

static const hal_mtd_methods_t psram_methods = {
    .read = __psram_read,
    .write = __psram_write,
    .ioctl = __psram_ioctl,
};

static int __psram_open(hal_dev_t *dev, int oflag, va_list valist)
{
    psram_dev_t *psram = (psram_dev_t *)dev;

    if (__stm32_psram_init(psram) != 0) {
        return -1;
    }

    return 0;
}

static hal_dev_ops_t dev_ops = {
    .open = __psram_open,
};

static int stm32_psram_init(void)
{
    psram_dev.mtd.total_size = PSRAM_DEVICE_SIZE;
    if (hal_mtd_register(&psram_dev.mtd, "psram", &psram_methods, 0) != 0) {
        return -1;
    }
    if (hal_dev_init((hal_dev_t *)&psram_dev, &dev_ops) != 0) {
        return -2;
    }
    return 0;
}

MODULE_INIT(driver, stm32_psram_init);
